

#[cfg(test)]
mod test{
    use libusb_src as ffi;

    #[test]
    fn test_version() {
        use std::{ffi::CStr, str};
        let version = unsafe { &*ffi::libusb_get_version() };
        let rc = str::from_utf8(unsafe { CStr::from_ptr(version.rc) }.to_bytes()).unwrap_or("");
        let describe =
            str::from_utf8(unsafe { CStr::from_ptr(version.describe) }.to_bytes()).unwrap_or("");

        assert_eq!(version.major, 1);
        assert_eq!(version.minor, 0);
        println!(
            "libusb v{}.{}.{}.{}{} {}",
            version.major, version.minor, version.micro, version.nano, rc, describe
        );
    }

    #[test]
    fn test_init_and_exit() {
        let mut context: *mut ffi::libusb_context = std::ptr::null_mut();
        for i in 0..=100 {
            match unsafe { ffi::libusb_init(&mut context) } {
                0 => (),
                err => panic!("Failed to init libusb on iteration {}: {}", i, err),
            }
            unsafe {
                ffi::libusb_exit(context);
            }
            #[cfg(target_os = "macos")]
            std::thread::sleep(std::time::Duration::from_millis(1));
            context = std::ptr::null_mut();
        }
    }

    #[test]
    fn test_get_device_list() {
        let mut context = std::mem::MaybeUninit::<*mut ffi::libusb_context>::uninit();
        match unsafe { ffi::libusb_init(context.as_mut_ptr()) } {
            0 => (),
            err => panic!("Failed to init libusb {}", err),
        }
        let mut list = std::mem::MaybeUninit::<*const *mut ffi::libusb_device>::uninit();
        let list_size =
            unsafe { ffi::libusb_get_device_list(context.assume_init(), list.as_mut_ptr()) };
        if list_size < 0 || unsafe { list.assume_init().is_null() } {
            panic!("Failed to get device list {} {:p}", -list_size, unsafe {
                list.assume_init()
            });
        }
        unsafe {
            ffi::libusb_free_device_list(list.assume_init(), 1);
        }
        unsafe {
            ffi::libusb_exit(context.assume_init());
        }
    }

    #[test]
    fn test_fill_control_setup() {
        let mut buf = [0u8; ffi::LIBUSB_CONTROL_SETUP_SIZE + 1];
        unsafe {
            ffi::libusb_fill_control_setup(
                buf.as_mut_ptr(),
                ffi::LIBUSB_REQUEST_TYPE_VENDOR | ffi::LIBUSB_ENDPOINT_OUT,
                0x04,
                0x4e,
                0,
                (buf.len() - ffi::LIBUSB_CONTROL_SETUP_SIZE) as u16,
            );
        }
        buf[ffi::LIBUSB_CONTROL_SETUP_SIZE] = 0x01;
        let setup: *mut ffi::libusb_control_setup = buf.as_mut_ptr() as *mut _;

        assert_eq!(
            unsafe { (*setup).bmRequestType },
            ffi::LIBUSB_REQUEST_TYPE_VENDOR | ffi::LIBUSB_ENDPOINT_OUT
        );
        assert_eq!(unsafe { (*setup).bRequest }, 0x04);
        assert_eq!(unsafe { u16::from_le((*setup).wValue) }, 0x4e);
        assert_eq!(unsafe { u16::from_le((*setup).wIndex) }, 0);
        assert_eq!(unsafe { u16::from_le((*setup).wLength) }, 1);
    }

    #[test]
    fn test_fill_control_transfer() {
        extern "system" fn callback(_transfer: *mut ffi::libusb_transfer) {}

        let mut buf = [0u8; ffi::LIBUSB_CONTROL_SETUP_SIZE + 1];
        unsafe {
            ffi::libusb_fill_control_setup(
                buf.as_mut_ptr(),
                ffi::LIBUSB_REQUEST_TYPE_VENDOR | ffi::LIBUSB_ENDPOINT_OUT,
                0x04,
                0x4e,
                0,
                (buf.len() - ffi::LIBUSB_CONTROL_SETUP_SIZE) as u16,
            );
        }
        buf[ffi::LIBUSB_CONTROL_SETUP_SIZE] = 0x05;

        let mut transfer = std::mem::MaybeUninit::<ffi::libusb_transfer>::uninit();

        unsafe {
            ffi::libusb_fill_control_transfer(
                transfer.as_mut_ptr(),
                std::ptr::null_mut(),
                buf.as_mut_ptr(),
                callback,
                std::ptr::null_mut(),
                1000,
            );
        }
        let transfer = unsafe { &mut transfer.assume_init() };
        assert_eq!(transfer.endpoint, 0);
        assert_eq!(
            transfer.length as usize,
            ffi::LIBUSB_CONTROL_SETUP_SIZE + 1
        );
        assert_eq!(transfer.timeout, 1000);
        assert_eq!(
            transfer.transfer_type,
            ffi::LIBUSB_TRANSFER_TYPE_CONTROL
        );
        assert_eq!(transfer.buffer, buf.as_mut_ptr());

        let data = unsafe {
            std::slice::from_raw_parts(ffi::libusb_control_transfer_get_data(transfer as *mut _), 1)
        };
        assert_eq!(data[0], 0x05);
    }

    #[test]
    fn test_fill_bulk_transfer() {
        extern "system" fn callback(_transfer: *mut ffi::libusb_transfer) {}

        let mut transfer = std::mem::MaybeUninit::<ffi::libusb_transfer>::uninit();
        let mut buf = [5u8; 64];
        unsafe {
            ffi::libusb_fill_bulk_transfer(
                transfer.as_mut_ptr(),
                std::ptr::null_mut(),
                0x80,
                buf.as_mut_ptr(),
                buf.len() as libc::c_int,
                callback,
                std::ptr::null_mut(),
                1000,
            );
        }
        let transfer = unsafe { &transfer.assume_init() };
        assert_eq!(transfer.endpoint, 0x80);
        assert_eq!(transfer.timeout, 1000);
        assert_eq!(
            transfer.transfer_type,
            ffi::LIBUSB_TRANSFER_TYPE_BULK
        );
        assert_eq!(transfer.buffer, buf.as_mut_ptr());
        assert_eq!(transfer.length, buf.len() as libc::c_int);
    }
}

